# vim:ts=4:sw=4:noet:
""" Stacktrace window """

import wx
from c.remote.simpleobj import *
from c.ui.wxw.widgets.baseframe import BaseFrame
from c.ui.wxw.inspector import InspectWindow

class ObjectLink(wx.HyperlinkCtrl):
	def __init__(self, parent, obj):
		wx.HyperlinkCtrl.__init__(self, parent, -1, str(obj), obj.loc)
		col  = wx.Colour(100,180,255)
		col0 = wx.Colour( 87,157,253)
		self.SetHoverColour(col)
		self.SetNormalColour(col0)
		self.SetVisitedColour(col0)
		f = self.GetFont()
		f.SetUnderlined(False)
		self.SetFont(f)

class StackTraceWindow(BaseFrame):
	def __init__(self, parent, remote, mem):
		self.r = remote
		self.parent = parent
		self.mem = mem

		BaseFrame.__init__(self, parent, "Stack Trace", size=(280, 320))
		self.SetMinSize((280, 320))
		
		self.panel = wx.Panel(self, -1)
		self.noLibraryFrames = True
		
		self.lblHdrError = wx.StaticText(self.panel, -1, "Error:")
		self.lblError = wx.StaticText(self.panel, -1, "")
		self.staticline1 = wx.StaticLine(self.panel, -1)
		self.staticline2 = wx.StaticLine(self.panel, -1)

		self.treeTrace = wx.TreeCtrl(self.panel, style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT)
		self.root = self.treeTrace.AddRoot("Stack Trace")
		self.treeTrace.SetIndent(0)
		
		self.lblHdrRecv = wx.StaticText(self.panel, -1, "Receiver:")
		self.lblHdrArgs = wx.StaticText(self.panel, -1, "Arguments:")
		self.lblHdrTemps = wx.StaticText(self.panel, -1, "Temporaries:")

		# props
		ft = self.lblError.GetFont()
		newft = wx.Font(ft.GetPointSize(), wx.DECORATIVE, wx.NORMAL, wx.BOLD, 0, ft.GetFaceName())

		self.lblHdrError.SetFont(newft)
		self.lblHdrRecv.SetFont(newft)
		self.lblHdrArgs.SetFont(newft)
		self.lblHdrTemps.SetFont(newft)
		# layout
		self.mainSizer = wx.BoxSizer(wx.VERTICAL)
		sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
		sizer_1.Add(self.lblHdrError, 0, wx.ALIGN_RIGHT, 0)
		sizer_1.Add(self.lblError, 0, wx.LEFT|wx.EXPAND, 5)
		self.mainSizer.Add(sizer_1, 0, wx.ALL|wx.EXPAND, 5)
		self.mainSizer.Add(self.staticline1, 0, wx.EXPAND, 0)
		self.mainSizer.Add(self.treeTrace, 1, wx.EXPAND, 0)
		self.mainSizer.Add(self.staticline2, 0, wx.EXPAND, 0)
		self.recvSizer = wx.BoxSizer(wx.HORIZONTAL)
		self.recvPanel = wx.Panel(self.panel, -1)
		self.argsSizer = wx.BoxSizer(wx.HORIZONTAL)
		self.argsPanel = wx.Panel(self.panel, -1)
		self.tempsSizer = wx.BoxSizer(wx.HORIZONTAL)
		self.tempsPanel = wx.Panel(self.panel, -1)
		gridsizer_1 = wx.FlexGridSizer(3,2,5,5)
		gridsizer_1.Add(self.lblHdrRecv, 0, wx.ALIGN_RIGHT, 0)
		gridsizer_1.Add(self.recvPanel, 0, wx.EXPAND, 0)
		gridsizer_1.Add(self.lblHdrArgs, 0, wx.ALIGN_RIGHT, 0)
		gridsizer_1.Add(self.argsPanel, 0, wx.EXPAND, 0)
		gridsizer_1.Add(self.lblHdrTemps, 0, wx.ALIGN_RIGHT, 0)
		gridsizer_1.Add(self.tempsPanel, 0, wx.EXPAND, 0)
		self.recvPanel.SetSizer(self.recvSizer)
		self.argsPanel.SetSizer(self.argsSizer)
		self.tempsPanel.SetSizer(self.tempsSizer)
		self.mainSizer.Add(gridsizer_1, 0, wx.ALL|wx.EXPAND, 5)
		self.panel.SetSizer(self.mainSizer)

		self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.treeTrace)
		self.Bind(wx.EVT_CLOSE, self.onClose)
		self.Bind(wx.EVT_HYPERLINK, self.onHyperlink)

	def onHyperlink(self, event):
		url = event.GetURL()
		if "@" in url:
			name, ptr = url.split("@")
			InspectWindow(self, self.mem, self.r, int(ptr), name)
		
	def onClose(self, event):
		self.Show(False)

	def hide(self):
		self.Show(False)

	def showBacktrace(self,msg):
		self.Show(True)
		msg = msg.rstrip().strip("Error: ")
		if "Runtime error (" in msg:
			knownErrors = {
					"BlkArg"       : "Incorrect number of block arguments",
					"ArrEmpty"     : "Empty collection",
					"NoElt"        : "Invalid array index",
					"NoKey"        : "No key in dictionary",
					"PrimFail"     : "Fatal primitive error",
					"SubclassResp" : "Subclass' responsibility",
					"ArrIdx"       : "Invalid array index",
					"DivZero"      : "Division by zero",
					"Overflow"     : "Numeric overflow",
					}
			rte = msg[18:-1]
			if rte in knownErrors:
				msg = knownErrors[rte]
		self.lblError.SetLabel(msg)
		self.treeTrace.DeleteChildren(self.root)
		self.frames = self.r.backtrace(mem=self.mem)
		ids = self.addFrames(self.frames)
		if len(ids)>0:
			self.treeTrace.SelectItem(ids[0], True)
		self.treeTrace.SetFocus()
     
	def addFrames(self, frames):
		ids = []
		for i,frame in enumerate(frames):
			clsItem = self.parent.project.controller.getProjectItem(frame.incls)
			if clsItem:
				if self.noLibraryFrames and clsItem.pkgitem.name!="Project":
					continue
			text = "%d: %s>>%s at line %d" % (i, frame.incls, frame.meth, frame.line)
			ids.append(self.treeTrace.AppendItem(self.root, text))
		return ids
	
	def selectFrame(self, text):
		sname = text.split(":")
		if len(sname)==1:
			return
		if sname[0]=="arguments":
			return
		frame = self.frames[int(sname[0])]
		r = frame.args[0]
		self.recvSizer.Clear(True)
		self.recvSizer.Add(ObjectLink(self.recvPanel, r), 0, wx.RIGHT|wx.EXPAND, 5)
		self.argsSizer.Clear(True)
		for a in frame.args[1:]:
			self.argsSizer.Add(ObjectLink(self.argsPanel, a), 0, wx.RIGHT|wx.EXPAND, 5)
		self.tempsSizer.Clear(True)
		for t in frame.temps:
			self.tempsSizer.Add(ObjectLink(self.tempsPanel, t), 0, wx.RIGHT|wx.EXPAND, 5)
		self.tempsPanel.Layout()
		self.parent.showSource(frame.incls, frame.meth, frame.line-1)
		self.recvSizer.Fit(self.recvPanel)
		self.argsSizer.Fit(self.argsPanel)
		self.tempsSizer.Fit(self.tempsPanel)

	def OnSelChanged(self, evt):
		item = evt.GetItem()
		self.selectFrame(self.treeTrace.GetItemText(item))
